package com.caishi.lkx.exam.api;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.caishi.lkx.common.AdminBaseApi;
import com.caishi.lkx.common.ienum.DeviceType;
import com.caishi.lkx.common.model.Seo;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.exam.dto.SelectCategoryListDto;
import com.caishi.lkx.exam.model.*;
import com.caishi.lkx.exam.service.*;
import com.caishi.lkx.exam.vo.SpecialConditionVo;
import com.caishi.lkx.exam.vo.UserChooseCategoryVo;
import com.caishi.lkx.user.dto.GroupPermission;
import com.caishi.lkx.user.model.GroupModel;
import com.caishi.lkx.user.model.MemberModel;
import com.caishi.lkx.user.service.IGroupService;
import com.caishi.lkx.user.service.IMemberService;
import com.zzw.common.threadpool.SysThreadPool;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.entity.impl.BaseIntEntity;
import com.zzw.common.exception.BizException;

import com.caishi.lkx.exam.ienum.type.ColumnType;
import com.caishi.lkx.exam.records.RecordsGetDataService;

import com.caishi.lkx.order.ienum.status.AssetsStatus;
import com.caishi.lkx.order.ienum.type.AssetsType;
import com.caishi.lkx.order.model.UserAssetsDataModel;
import com.caishi.lkx.order.service.IUserAssetsDataService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;


@Tag(name = "科目相关")
@Slf4j
@RestController
@RequestMapping("/exam/category")
public class CategoryApi extends AdminBaseApi<ICategoryService, CategoryModel, Long> {

    @Resource
    private ICategoryService service;

    @Resource
    private IExamService examService;

    @Resource
    private IIndustryService industryService;

    @Resource
    private IColumnService columnService;
    @Resource
    private IQuestionService questionService;
    @Resource
    private IMemberService memberService;

    @Resource
    private IUserAssetsDataService userAssetsDataService;

    @Resource
    private ICategoryService categoryService;
    @Resource
    private IChapterService chapterService;

    @Resource
    private RecordsGetDataService recordsGetDataService;
    @Resource
    private IGroupService groupService;
    @Resource
    private IPaperService paperService;
//    @DubboReference
//    private RpcCategoryBaseService rpcCategoryBaseService;

    @Override
    public ICategoryService loadService() {
        return service;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CategoryModel add(@RequestBody CategoryModel entity) {
        entity = super.add(entity);
        columnService.categoryUpdateColumn(entity.getId(), entity.getColumns());
        return entity;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CategoryModel update(@RequestBody CategoryModel entity) {
        columnService.categoryUpdateColumn(entity.getId(), entity.getColumns());
        return super.update(entity);
    }

    @Override
    @Roles
    public List<CategoryModel> list(@RequestParam(defaultValue = "10") Integer size, @ModelAttribute CategoryModel model, @NotNull JSONObject otherParam) {
        if (size > 3000) {
            size = 3000;
        }
        var res = listCover(loadService().selectList(loadService().wrapperProcessing(baseWrapper(model, otherParam).last(" limit " + size))), otherParam);
        var dt = userContext.currentTokenData().getDevice();
        if (dt != DeviceType.admin && userContext.loginStatus() && StrUtil.isNotBlank(model.getExamId())) {
            var examId = model.getExamId();
            return clientCategories(res, examId, false);
        }
        return res;
    }

    @GetMapping("/current-choose")
    @Roles
    @Operation(summary = "获取当前用户选择的科目")
    public UserChooseCategoryVo currentUserChoose() throws ExecutionException, InterruptedException {
        String userId = userContext.currentUserId();
        Long groupId = userContext.currentLocalGroupId();
        CategoryModel categoryModel = null;
        boolean def = true;
        if (StrUtil.isNotBlank(userId)) {
            var member = memberService.getById(userId);
            if (member != null && member.getCategoryId() != null) {
                def = false;
                categoryModel = service.getById(member.getCategoryId());
            }
        }
        if (StrUtil.isBlank(userId) && null != groupId) {
            GroupModel groupModel = groupService.getById(groupId);
            if (null != groupModel.getPermissionMap()) {
                Optional<String> industryId = groupModel.getPermissionMap().parallelStream().map(f -> {
                    if (f.getType() == GroupPermission.Type.zhiye && f.getEnable()) {
                        Optional<String> first = f.getIndustryActionIds().stream().findFirst();
                        return first.orElse(null);
                    }
                    return null;

                }).filter(Objects::nonNull).findFirst();
                if (industryId.isPresent()) {
                    categoryModel = service.selectFirst(Wrappers.<CategoryModel>lambdaQuery().eq(CategoryModel::getIndustryId, industryId.get()).orderByAsc(CategoryModel::getCreatedTime));
                }
            }

        }
        if (categoryModel == null) {
            categoryModel = service.selectFirst(service.wrappers().apply("attribute & 5 = 5").lt(CategoryModel::getSort, 100).orderByAsc(CategoryModel::getSort));
        }
        if (categoryModel == null) {
            categoryModel = service.selectFirst(service.wrappers().apply("attribute & 5 = 5").orderByAsc(CategoryModel::getSort));
        }
        if (categoryModel == null) {
            categoryModel = service.selectFirst(service.wrappers().orderByAsc(CategoryModel::getSort));
        }
        CategoryModel finalCategoryModel = categoryModel;
        if (finalCategoryModel == null) {
            return null;
        }
        Future<ExamModel> exam = SysThreadPool.submit(() -> examService.getById(finalCategoryModel.getExamId()));
        Future<IndustryModel> industry = SysThreadPool.submit(() -> industryService.getById(finalCategoryModel.getIndustryId()));
        return UserChooseCategoryVo.builder().categoryId(categoryModel.getId()).categoryCode(categoryModel.getCode()).categoryName(categoryModel.getName()).examId(exam.get().getId()).examCode(exam.get().getCode()).examName(exam.get().getName()).industryId(industry.get().getId()).industryCode(industry.get().getCode()).industryName(industry.get().getName()).def(def).build();
    }

    @GetMapping("/leaf-detail")
    @Operation(summary = "行业 考试 科目详情")
    @Roles
    public List<Seo> indExamCategoryDetail(@RequestParam String industryCode, String examCode, String categoryCode) throws ExecutionException, InterruptedException {
        List<Future<Seo>> futures = new LinkedList<>();
        List<Seo> result = new LinkedList<>();
        futures.add(SysThreadPool.submit(() ->  industryService.getById(industryCode)));
        if (StrUtil.isNotEmpty(examCode)) {
            futures.add(SysThreadPool.submit(() -> examService.getById(examCode)));
        }
        if (StrUtil.isNotEmpty(categoryCode)) {
            futures.add(SysThreadPool.submit(() -> service.getById(Long.valueOf(categoryCode))));
        }
        for (var r : futures) {
            result.add(r.get());
        }
        return result;
    }


    @Roles
    @PostMapping("/setting-choose")
    @Operation(summary = "设置用户选择的科目")
    public void settingUserChoose(@RequestBody SelectCategoryListDto selectCategoryListDto) {
        MemberModel memberModel = new MemberModel();
        memberModel.setSelectCategory(selectCategoryListDto.getCategoryList());
        memberModel.setId(userContext.currentUserId());
        memberModel.setCategoryId(selectCategoryListDto.getCategoryList().get(0).getCategoryId());
        memberService.update(memberModel);
    }

    @Roles
    @GetMapping("/category-flag")
    @Operation(summary = "获取用户科目购买标志")
    public boolean categorySoldFlag(@RequestParam Long categoryId) throws BizException {
        Long localGroupId = userContext.currentLocalGroupId();
        Long userGroupId = userContext.currentGroupId();
        return categoryService.currentHaveAuth(userGroupId, null != userGroupId ? null : localGroupId, userContext.currentUserId(), categoryId, userContext.currentTokenData().getClientType());
    }


    @GetMapping("/sold-list")
    @Operation(summary = "获取当前用户购买的科目")
    public List<CategoryModel> userSoldCategory() {
        String userId = userContext.currentUserId();
        var assets = userAssetsDataService.userAsset(userId, AssetsType.category, null).stream().filter(v -> v.getStatus() != AssetsStatus.invalid).toList();
        if (assets.isEmpty()) return Collections.emptyList();
        var categories = service.selectList(service.wrapperProcessing(service.wrappers().in(BaseIntEntity::getId, assets.stream().map(UserAssetsDataModel::getAssetMappingId).toList())));
        if (categories.isEmpty()) return categories;
        var authFlagMap = assets.stream().collect(Collectors.toMap(UserAssetsDataModel::getAssetMappingId, v -> v.getStatus() == AssetsStatus.effective, (v, v1) -> v || v1));
        var examMap = examService.selectList(examService.wrappers().select(BaseEntity::getId, ExamModel::getName).in(BaseEntity::getId, categories.stream().map(CategoryModel::getExamId).toList())).stream().collect(Collectors.toMap(BaseEntity::getId, ExamModel::getName));
        return categories.parallelStream().peek(p -> {
            p.setCategoryRecords(recordsGetDataService.getCategoryCountRecordsResult(userId, p.getIndustryId(), p.getExamId(), p.getId()));
            p.setExamName(examMap.get(p.getExamId()));
            p.setIndustryName(industryService.queryName(p.getIndustryId()));
            p.setAuthFlag(Boolean.TRUE.equals(authFlagMap.get(p.getId().toString())) ? 0x01 : (byte) 0x00);
        }).toList();
    }

    @GetMapping("/leaf-list")
    @Roles
    @Operation(summary = "获取考试下科目")
    @Parameters({
            @Parameter(name = "examId", description = "考试Id", required = true),
            @Parameter(name = "parentId", description = "父级科目id", required = true)
        })
    public List<CategoryModel> examAllLeafCategory(@RequestParam String examId, @RequestParam(defaultValue = "0") long parentId) {
        var res = service.selectList(service.wrapperProcessing(service.wrappers().eq(CategoryModel::getExamId, examId).eq(CategoryModel::getShowState, 1).eq(CategoryModel::getParentId, parentId)));
        return clientCategories(res, examId, true);
    }


    @GetMapping("/leaf-list-pc")
    @Roles
    @Operation(summary = "获取考试下科目")
    @Parameters({
            @Parameter(name = "examId", description = "考试Id", required = true),
            @Parameter(name = "parentId", description = "父级科目id", required = true)
    })
    public List<CategoryModel> examAllLeafCategoryPc(@RequestParam String examId) {

        return service.selectList(service.wrapperProcessing(service.wrappers().eq(CategoryModel::getExamId, examId).eq(CategoryModel::getShowState, 1)));
    }


    @GetMapping("/generate-condition")
    @Roles
    public SpecialConditionVo generateConditionResult(@RequestParam Long categoryId, @RequestParam(required = false) List<String> ids, Integer type) {
        List<String> inIds = new ArrayList<>();
        if ((null == ids || ids.isEmpty()) && type != 4) {
            inIds = categoryService.categoryRelationQuestionIds(categoryId);
        } else {
            inIds = switch (type) {
                case 1 -> paperService.getQuestionIdsByPaper(ids);
                case 2 -> chapterService.getQuestionIdsByChapter(ids);
                case 4 -> questionService.getScatteredQuestion(categoryId, false);
                default -> categoryService.categoryRelationQuestionIds(categoryId);
            };
        }
        return categoryService.categoryGenerateCollectCondition(null, inIds);
    }

    @GetMapping("/chapterTree")
    @Operation(summary = "获取科目下所有章节")
    public List<ChapterModel> allChapterTreeByCategoryId(@RequestParam Long categoryId) {
        List<String> chapterIds = categoryService.columnRelationCollectionByCategory(categoryId, ColumnType.chapterPractice);
        if (chapterIds.isEmpty()) return new ArrayList<>();
        List<ChapterModel> chapterModels = chapterService.selectList(Wrappers.<ChapterModel>lambdaQuery().in(ChapterModel::getId, chapterIds).select(ChapterModel::getName, ChapterModel::getId, ChapterModel::getParentId, ChapterModel::getLeaf, ChapterModel::getPath));
        for (ChapterModel chapterModel : chapterModels) {
            chapterService.setChildren(chapterModel, -1, Wrappers.<ChapterModel>lambdaQuery().select(ChapterModel::getName, ChapterModel::getId, ChapterModel::getParentId, ChapterModel::getLeaf, ChapterModel::getPath));
        }
        return chapterModels;
    }

    @Override
    protected LambdaQueryWrapper<CategoryModel> baseWrapper(CategoryModel model, JSONObject otherParam) {
        String searchKey = otherParam.getStr("searchKey");
        boolean noJgtk = otherParam.getBool("noJgtk", false);
        boolean tree = otherParam.getBool("tree", false);
        boolean noChapter = otherParam.getBool("noChapter", false);
        boolean noPaper = otherParam.getBool("noPaper", false);
        boolean noSpecialPractice = otherParam.getBool("noSpecialPractice", false);
        boolean noRandomFormPaper = otherParam.getBool("noRandomFormPaper", false);
        var notInIds = new LinkedList<>();
        if (noChapter || noPaper || noSpecialPractice || noRandomFormPaper) {
            var types = new ArrayList<>(8);
            if (noChapter) types.add(ColumnType.chapterPractice);
            if (noPaper) types.add(ColumnType.paperLibrary);
            if (noSpecialPractice) types.add(ColumnType.specialPractice);
            if (noRandomFormPaper) types.add(ColumnType.randomFormPaper);
            notInIds.addAll(columnService.selectList(columnService.wrappers().select(ColumnModel::getCategoryId).in(types.size() < 4, ColumnModel::getType, types).groupBy(ColumnModel::getCategoryId)).stream().map(ColumnModel::getCategoryId).toList());
        }
        return super.baseWrapper(model, otherParam).like(StrUtil.isNotBlank(searchKey), CategoryModel::getName, searchKey).ne(noJgtk && StrUtil.isBlank(model.getIndustryId()), CategoryModel::getIndustryId, ExamModel.GJTK).notIn(!notInIds.isEmpty(), BaseIntEntity::getId, notInIds).apply(!notInIds.isEmpty(), "  attribute & 1 = 1").eq(tree, CategoryModel::getParentId, 0);
    }

    @Override
    protected List<CategoryModel> listCover(List<CategoryModel> result, JSONObject otherParam) {
        boolean haveColumns = otherParam.getBool("haveColumns", false);
        boolean tree = otherParam.getBool("tree", false);
        boolean haveDetailName = otherParam.getBool("haveDetailName", false);
        boolean columnFlag = otherParam.getBool("columnFlag", false);
        var work = new Function<CategoryModel, CategoryModel>() {
            @Override
            public CategoryModel apply(CategoryModel c) {
                var work1 = new Consumer<CategoryModel>() {
                    @Override
                    public void accept(CategoryModel model) {
                        if (haveDetailName) {
                            if (model.getIndustryName() == null) {
                                model.setIndustryName(industryService.queryName(c.getIndustryId()));
                                model.setExamName(examService.queryName(c.getExamId()));
                            }
                        }
                        if (haveColumns) {
                            model.setColumns(columnService.queryCategoryColumns(model.getId()));
                        }
                        if (columnFlag) {
                            model.setHaveColumn(service.haveColumn(model.getId()));
                        }
                    }
                };
                work1.accept(c);
                if (tree) {
                    service.treeProcess(c, (child, parent) -> {
                        child.setIndustryName(c.getIndustryName());
                        child.setExamName(c.getExamName());
                        work1.accept(child);
                    });
                }
                return c;
            }
        };

        if (tree) {
            result = result.parallelStream().map(c -> {
                c = service.setChildren(c, -1);
                return work.apply(c);
            }).toList();
        } else {
            result.parallelStream().forEach(work::apply);
        }
        return super.listCover(result, otherParam);
    }


    private List<CategoryModel> clientCategories(List<CategoryModel> res, String examId, Boolean onlyLeaf) {
//        if (res.isEmpty()) return res;
        var authIds = onlyLeaf ? categoryService.userHaveCategory(userContext.currentUserId()) : categoryService.userHaveCategoryTree(userContext.currentUserId());
//        var cids = authIds.stream().sorted(Long::compareTo).toArray(Long[]::new);
//        res.sort(IndustryServiceImpl.permissionComparator(cids));
//        String userId = userContext.currentUserId();
//        List<Long> selectCategoryIds = new ArrayList<>();
//        if(StrUtil.isNotBlank(userId)){
//            var member = memberService.getById(userId);
//            selectCategoryIds = member.getSelectCategory()!=null && !member.getSelectCategory().isEmpty()?member.getSelectCategory().stream().map(SelectCategoryDto::getCategoryId).toList():new ArrayList<>();
//        }
//
//        for (var i : res) {
//            var k =  (CommonUtil.binarySearch(cids, i.getId()) ? 0x01 : 0x00);
//            i.setAuthFlag((byte) k);
//            if (selectCategoryIds.contains(i.getId()))
//                i.setSelected(true);
//
//        }


        return res;
    }

//    @GetMapping("ytk-course")
//    @Roles
//    public CoursePathDto getCoursePath(String examId, Integer categoryId) {
//        return rpcCategoryBaseService.getCoursePath(examId, categoryId);
//    }

}
